// tge.rs
// copyright zipxing@hotmail.com 2022~2023

//!
//! sequence_frames.rs实现了一个SDL模式下的序列帧结构SequenceFrames
//! 对应的文件后缀为.ssf, 参考**gen_sdl_seqs**脚本, 这个脚本
//! 把gif文件转换成ssf文件, 为了控制文件size，每帧进行了
//! gzip压缩
//!
//! # Example
//!
//! ```
//!
//! // let mut ssf = SequenceFrames::new();
//! // ssf.load_ssffile("assets/sdq/dance.ssf").unwrap();
//!
//! // // 在render的draw里循环播放序列帧
//! // fn draw<G: Model>(&mut self, context: &mut Context, _model: &mut G, _dt: f32) {
//! //     self.ssf.set_sprite_frame(
//! //         // 从sprites集合里按tag取出一个sprite
//! //         &mut self.sprites.get_by_tag("SNAKE"),
//! //         // 三帧更新一个序列帧
//! //         (context.stage / 3 % self.ssf.frame_count as u32) as usize,
//! //         1,
//! //         1,
//! //     );
//! //     //...
//! // }
//!
//! ```
//!
//! 上述代码包含在snake游戏中，可以运行查看直观效果
//! $ cargo run --bin snake --features sdl
//!

use crate::{
    render::adapter::sdl::sdlsym,
    render::style::{Color, Style},
    util::get_abs_path,
};
use flate2::read::GzDecoder;
use regex::Regex;
use std::{
    fs::File,
    io::{BufRead, BufReader, Read},
};

use crate::render::sprite::DatFileError;
use crate::render::sprite::Sprite;
use crate::render::image::escstr_to_buffer;

pub struct SequenceFrames {
    pub width: u16,
    pub height: u16,
    pub texture_id: u16,
    pub frame_count: u16,
    pub frame_len: Vec<u32>,
    pub frame_offset: Vec<u32>,
    pub frame_data: Vec<u8>,
}

impl SequenceFrames {
    pub fn new() -> Self {
        Self {
            width: 0,
            height: 0,
            texture_id: 0,
            frame_count: 0,
            frame_len: vec![],
            frame_offset: vec![],
            frame_data: vec![],
        }
    }

    /// texture_id == 257 表示ESC格式的序列帧,
    /// 每行数据为esc序列(参考fn escstr_to_buffer)
    ///
    /// texture_id == 256 表示utf8文本模式的序列帧,
    /// 每个cell的数据为不定长: fg bg utf8_bytes
    ///
    /// texture_id == 255 表示Sdl格式的帧数据,每个cell的纹理可以不同，
    /// cell的数据长度为3字节: fg bg(bg即cell_texture_id) sdlsym
    /// 当前系统中ssf文件基本上是这种格式
    ///
    /// texture_id < 255 表示Sdl格式的帧数据，每个cell的纹理都是texture_id,
    /// cell的数据长度为2字节: fg sdlsym
    pub fn load_ssffile(&mut self, fpath: &str) -> Result<(), DatFileError> {
        self.frame_len = vec![];
        self.frame_offset = vec![];
        self.frame_data = vec![];
        let fpstr = get_abs_path(fpath);
        let file = File::open(fpstr)?;
        let mut reader = BufReader::new(file);

        let re = Regex::new(r"width=(\d+),height=(\d+),texture=(\d+),frame_count=(\d+)").unwrap();
        let rel = Regex::new(r"(\d+),(.*?)").unwrap();
        let mut file_header = String::new();
        reader.read_line(&mut file_header)?;
        for cap in re.captures_iter(&file_header) {
            self.width = cap[1].parse::<u16>().unwrap();
            self.height = cap[2].parse::<u16>().unwrap();
            self.texture_id = cap[3].parse::<u16>().unwrap();
            self.frame_count = cap[4].parse::<u16>().unwrap();
        }
        let mut len_header = String::new();
        reader.read_line(&mut len_header)?;
        let mut offset = 0u32;
        for cap in rel.captures_iter(&len_header) {
            let flen = cap[1].parse::<u32>().unwrap();
            self.frame_len.push(flen);
            self.frame_offset.push(offset);
            offset += flen;
        }
        reader.read_to_end(&mut self.frame_data)?;
        Ok(())
    }

    pub fn set_sprite_frame(
        &self,
        sprite: &mut Sprite,
        frame_idx: usize,
        off_x: u16,
        off_y: u16,
    ) {
        let start = self.frame_offset[frame_idx] as usize;
        let flen = self.frame_len[frame_idx] as usize;
        let mut decoder = GzDecoder::new(&self.frame_data[start..start + flen]);

        if self.texture_id == 257 {
            let reader = BufReader::new(decoder);
            let mut row = 0;
            for line in reader.lines() {
                let l = line.unwrap();
                escstr_to_buffer(&l, &mut sprite.content, row, off_x, off_y);
                row += 1;
            }
        } else if self.texture_id == 256 {
            let mut decompressed_data = Vec::new();
            decoder.read_to_end(&mut decompressed_data).unwrap();
            let mut bpos = 0usize;
            let mut i = 0u16;
            loop {
                let fgc = decompressed_data[bpos];
                bpos += 1;
                let bgc = decompressed_data[bpos];
                bpos += 1;
                let mut utf8_bytes: Vec<u8> = vec![];
                let first = decompressed_data[bpos];
                bpos += 1;
                utf8_bytes.push(first);
                let mut blen = 0;
                if first >> 7 == 0 {
                    blen = 0;
                } else {
                    if first >> 5 == 0b0000_0110 {
                        blen = 1;
                    }
                    if first >> 4 == 0b0000_1110 {
                        blen = 2;
                    }
                    if first >> 3 == 0b0001_1110 {
                        blen = 3;
                    }
                }
                for _i in 0..blen {
                    utf8_bytes.push(decompressed_data[bpos]);
                    bpos += 1;
                }
                sprite.content.set_str(
                    i % self.width + off_x,
                    i / self.width + off_y,
                    std::str::from_utf8(&utf8_bytes).unwrap(),
                    Style::default()
                        .fg(Color::Indexed(fgc))
                        .bg(Color::Indexed(bgc)),
                );
                i += 1;
                if bpos == decompressed_data.len() {
                    break;
                }
            }
        } else {
            let mut decompressed_data = Vec::new();
            decoder.read_to_end(&mut decompressed_data).unwrap();
            let cell_len: usize = if self.texture_id == 255 { 3 } else { 2 };
            for i in 0..decompressed_data.len() as u16 / cell_len as u16 {
                let bgc: u8 = if self.texture_id == 255 {
                    decompressed_data[i as usize * cell_len + 2]
                } else {
                    self.texture_id as u8
                };
                sprite.content.set_str(
                    i % self.width + off_x,
                    i / self.width + off_y,
                    sdlsym(decompressed_data[i as usize * cell_len]),
                    Style::default()
                        .fg(Color::Indexed(decompressed_data[i as usize * cell_len + 1]))
                        .bg(Color::Indexed(bgc)),
                );
            }
        }
    }
}
